////////////////////////////////////////////////////////////////////////////////
//
//  The MIT License
//
//  Copyright (c) 2008 - 2009. Dimarik
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////

package ru.goodcore.transport
{

import flash.events.Event;
import flash.events.HTTPStatusEvent;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.net.URLVariables;
import flash.utils.ByteArray;

import ru.goodcore.logging.Log;
import ru.goodcore.logging.LogEventLevel;
import ru.goodcore.utils.ObjectUtil;

/**
 *  @author				Dimarik
 *  @version			1.0
 *  @langversion		3.0
 *  @playerversion		9.0
 *
 */
public class HTTPLongPollTransport extends AbstractTransport {

	//-------------------------------------------------------------------------
	//
	//   Private class's variables
	//
	//-------------------------------------------------------------------------

	//-------------------------------------------------------------------------
	//
	//   Class's methods
	//
	//-------------------------------------------------------------------------

	//-------------------------------------------------------------------------
	//
	//   Constructor
	//
	//-------------------------------------------------------------------------

	public function HTTPLongPollTransport() {
		super();

		this.loader.dataFormat = URLLoaderDataFormat.BINARY;
		this.loader.addEventListener(Event.COMPLETE, 					this.handler_success);
		this.loader.addEventListener(IOErrorEvent.IO_ERROR,				this.handler_fault);
		this.loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_fault);
		this.loader.addEventListener(HTTPStatusEvent.HTTP_STATUS,		this.handler_httpStatus);
	}



	//-------------------------------------------------------------------------
	//
	//   Properties
	//
	//-------------------------------------------------------------------------

	//---------------------------------
	//   url
	//---------------------------------
	private var _url:	String;

	public function get url():String {
		return this._url;
	}

	public function set url(value:String):void {
		if (this._url == value) return;
		this._url = value;
	}

	//---------------------------------
	//   method
	//---------------------------------
	private var _method:	String = URLRequestMethod.POST;

	public function get method():String {
		return this._method;
	}

	public function set method(value:String):void {
		if (this._method == value) return;
		this._method = value;
	}

	//---------------------------------
	//   dataFormat
	//---------------------------------
	private var _dataFormat:	String;

	public function get dataFormat():String {
		return this.loader.dataFormat;
	}

	public function set dataFormat(value:String):void {
		if (this.loader.dataFormat == value) return;
		this.loader.dataFormat = value;
	}

	//---------------------------------
	//   httpStatus
	//---------------------------------
	private var _httpStatus:	int;

	public function get httpStatus():int {
		return this._httpStatus;
	}

	//-------------------------------------------------------------------------
	//
	//   Protected constants
	//
	//-------------------------------------------------------------------------

	protected var loader:			URLLoader = new URLLoader();

	protected var currentRequest:	Object;

	//-------------------------------------------------------------------------
	//
	//   Private constants
	//
	//-------------------------------------------------------------------------

	private const _queue:			Array = new Array();

	//-------------------------------------------------------------------------
	//
	//   Private variables
	//
	//-------------------------------------------------------------------------

	//-------------------------------------------------------------------------
	//
	//   Public methods
	//
	//-------------------------------------------------------------------------

	//------------------------------------------
	// Common Remote Procedure Call
	//------------------------------------------
	public function call(apiMethod:String, responder:Object, options:Object = null):void {
		var requestObject:Object = new Object();
		requestObject.responder = responder;
		requestObject.method = apiMethod;
		requestObject.options = options;

		this._queue.push(requestObject);

		if( !this.currentRequest ) this.requestNext();
	}

	//------------------------------------------
	//  Load data
	//------------------------------------------
	public function load(responder:Object, options:Object = null):void {
		var requestObject:Object = new Object();
		requestObject.responder = responder;
		requestObject.method = '';
		requestObject.options = options;

		this._queue.push(requestObject);

		if( !this.currentRequest ) this.requestNext();
	}

	//-------------------------------------------------------------------------
	//
	//   Protected methods
	//
	//-------------------------------------------------------------------------

	/**
	 * @private
	 */
	protected function requestNext():void {
		if (this._queue.length == 0) return;

		this.currentRequest = this._queue.shift();

		var method:		String = this.currentRequest.method;
		var options:	Object = this.currentRequest.options;

		var params:Array = new Array();

		if (method) params['method'] = method;

		for (var i:String in options) {
			if (
				options[i] is String ||
				options[i] is Number ||
				options[i] is Boolean
			) {
				params[i] = options[i];
	    	} else {
	    		params[i] = this.convertToArray(options[i]);
	    	}
	    }

		var ba:ByteArray = new ByteArray();
	    ba.writeObject(params);

		// Debug info for server
		var vars:URLVariables = new URLVariables();
		if (params.hasOwnProperty('object')) vars['object'] = params.object;
		if (params.hasOwnProperty('action')) vars['action'] = params.action;

		if (params.hasOwnProperty('form')) {
			if (params.form.hasOwnProperty('code')) {
				vars['code'] = params.form.code;
			}
		}

		if (params.hasOwnProperty('viewer_id')) vars['viewer_id'] = params.viewer_id;
		

		var request:URLRequest = new URLRequest();
		request.url 		   = this._url + '?' + vars.toString();// + '?rnd=' + new Date().getTime();

		request.method 		   = this._method;
		request.data 		   = ba;

		var arr:Array = new Array(LogEventLevel.INFO, method + ' ' + ObjectUtil.toString(params));
		Log.getLogger("HTTP-LONGPOLL-OUT").log.apply(null, arr);

		this.loader.load(request);
	}

	//---------------------------------------------------------------------
	//
	//   Private methods
	//
	//---------------------------------------------------------------------

	private function convertToURLVariables(value:Object, vars:URLVariables = null):URLVariables {
		if (!vars) vars = new URLVariables();

		for (var i:String in value) {

			if ( 
				!(value[i] is String) &&
				!(value[i] is Number) &&
				!(value[i] is Boolean)
			) value[i] = this.convertToURLVariables(value[i]);

	    	vars[i] = value[i];
		}

		return vars;
	}

	private function convertToArray(value:Object):Array {
		var result:Array = new Array();

		for (var i:String in value) {

			if ( 
				!(value[i] is String) &&
				!(value[i] is Number) &&
				!(value[i] is Boolean)
			) value[i] = this.convertToArray(value[i]);

	    	result[i] = value[i];
		}

		return result;
	}

	//---------------------------------------------------------------------
	//
	//   Event handlers
	//
	//---------------------------------------------------------------------

	/**
	 * @private
	 */
	protected function handler_success(event:Event):void {
		this.currentRequest.responder.result( (event.target as URLLoader).data );
		this.currentRequest = null;
		this.requestNext();
	}

	/**
	 * @private
	 */
	protected function handler_fault(event:Event):void {
		var loader:URLLoader = event.currentTarget as URLLoader;
		this.currentRequest.responder.fault(loader.data);
		this.currentRequest = null;
		this.requestNext();
	}

	/**
	 * @private
	 */
	protected function handler_httpStatus(event:HTTPStatusEvent):void {
		this._httpStatus = event.status;
	}		


}
}